﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AMStudio.Driver.Omron;
using AMStudio.Common;
using System.Text.RegularExpressions;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using AMStudio.DelegateDispather;

namespace AMStudio.Driver
{
    public class OmornFinsDriver : IAutomationDriver
    {
        protected ExceptionManager ExManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();

        private String hardwareName = "PLC";
        public String HardwareName
        {
            get { return hardwareName; }
            set { hardwareName = value; }
        }
        private String hardwareEvent = "PLC_RET";
        public String HardwareEvent
        {
            get { return hardwareEvent; }
            set { hardwareEvent = value; }
        }

        private DelegateDispather.IEventClient eventClient;
        public DelegateDispather.IEventClient EventClient
        {
            get { return eventClient; }
            set { eventClient = value; }
        }
        private Dictionary<string, PLCPortsTag> PLCPorts = new Dictionary<string, PLCPortsTag>();
        private Dictionary<string, PLCPortsTag> ConfigurationPLCPorts = new Dictionary<string, PLCPortsTag>();
        private OmronFins hardwaredriver;
        private bool alreadyinitialized = false;
        private IExecuteReader result = new ExecuteReader(false);
        private OmronPLCConfig myConfig=new OmronPLCConfig();

        private void InitPLCPorts()
        {
            PLCPortsTagDAL db = new PLCPortsTagDAL();
            ConfigurationPLCPorts = db.SelectAllMapper();
            foreach (PLCPortsTag tag in ConfigurationPLCPorts.Values)
            {
                tag.ByteData = new Byte[tag.Length * 2];
            }
        }
        public virtual int ExecuteNonQuery(string command, params object[] parameters)
        {
            return -1;
        }
        public virtual IExecuteReader ExecuteReader(string command, params object[] parameters)
        {
            result.Success = false;
            try
            {
                switch (command)
                {
                    case "INIT":
                        {
                            #region "INIT"
                            if (alreadyinitialized)
                            {
                                result.Success = true;
                            }
                            InitPLCPorts();
              
                            IEventManager em = parameters[0] as IEventManager;
                            eventClient = new EventClient(em);
                            myConfig = parameters[1] as OmronPLCConfig;

                            FinsConfigData finsConfig = new FinsConfigData();
                            finsConfig.Address = myConfig.Address;
                            finsConfig.NodeAddress = myConfig.NodeAddress;
                            finsConfig.UnitAddress = myConfig.UnitAddress;
                            finsConfig.CommTimeout = myConfig.CommTimeout;
                            FinsEthernetClient client = new FinsEthernetClient(
                                myConfig.IPAddress,myConfig.TcpPort,myConfig.CommTimeout);
                            hardwareName =myConfig.HardwareName;
                            hardwareEvent =myConfig.HardwareEvent;
                            Logger.Write("Start to init hardware " + hardwareName);
                            hardwaredriver = new OmronFins(finsConfig, client);
                            if (!hardwaredriver.Initialize())
                            {
                                RaiseEvent("DISCONNECT", true, null);
                                //Logging.WriteError("System Error! (Can not connect the dialtable PLC controller!)");
                                hardwaredriver.Close();
                                result.Success = false;
                                alreadyinitialized = false;
                            }
                            else
                            {
                                RaiseEvent("CONNECT", true, null);
                                result.Success = true;
                                alreadyinitialized = true;
                            }
                            #endregion
                        }
                        break;
                    case "CYCLE_REST":
                        {
                            #region "CYCLE_REST"
                            foreach (PLCPortsTag configTag in ConfigurationPLCPorts.Values)
                            {
                                configTag.AlreadyReadForCycle = false;
                            }
                            foreach (PLCPortsTag tag in PLCPorts.Values)
                            {
                                tag.AlreadyReadForCycle = false;
                            }
                            #endregion
                        }
                        break;
                    case "READ":
                        {
                            #region Read
                            string tagId = parameters[0].ToString() + "_" + parameters[1].ToString();
                            PLCPortsTag tag ;
                            if (!PLCPorts.ContainsKey(tagId))
                            {
                                tag = ParseStringAddress(parameters[0].ToString(), (int)parameters[1]);
                                tag.ParentTag = FindParentTag(tag);
                                PLCPorts.Add(tagId, tag);
                            }
                            else
                            {
                                tag = PLCPorts[tagId];
                            }
                            if (!alreadyinitialized)
                            {
                                result.Success = false;
                                result.Result = tag.GetData();
                            }
                            else
                            {
                                result.Result = ReadTag(tag);
                                result.Success = true;
                            }
                            #endregion
                        }
                        break;
                    case "WRITE":
                        {
                            #region Write
                            if (!alreadyinitialized)
                            {
                                break;
                            }
                            string tagId = parameters[0].ToString() + "_" + parameters[1].ToString();
                            PLCPortsTag tag;
                            if (!PLCPorts.ContainsKey(tagId))
                            {
                                tag = ParseStringAddress(parameters[0].ToString(), (int)parameters[1]);
                                tag.ParentTag = FindParentTag(tag);
                                PLCPorts.Add(tagId, tag);
                            }
                            else
                            {
                                 tag = PLCPorts[tagId];
                            }
                            tag.SetData(parameters[2]);
                            if (alreadyinitialized)
                            {
                                hardwaredriver.Write(tag);
                                result.Success = true;
                            }
                            else
                            {
                                //hardwaredriver.Write(tag);
                                result.Success = false;
                            }
                            #endregion
                        }
                        break;
                    case "WRITE_STRINGVALUE":
                        {
                            #region Write StringValue
                            if (!alreadyinitialized)
                            {
                                break;
                            }
                            string tagId = parameters[0].ToString() + "_" + parameters[1].ToString();
                            PLCPortsTag tag;
                            if (!PLCPorts.ContainsKey(tagId))
                            {
                                tag = ParseStringAddress(parameters[0].ToString(), (int)parameters[1]);
                                tag.ParentTag = FindParentTag(tag);
                                PLCPorts.Add(tagId, tag);
                            }
                            else
                            {
                                tag = PLCPorts[tagId];
                            }
                            tag.SetData(parameters[2].ToString());
                            if (alreadyinitialized)
                            {
                                hardwaredriver.Write(tag);
                                result.Success = true;
                            }
                            else
                            {
                                //hardwaredriver.Write(tag);
                                result.Success = false;
                            }
                            #endregion
                        }
                        break;
                    case "RISING_EDGE":
                        {
                            #region RISING_EDGE
                            string tagId = parameters[0].ToString() + "_" + parameters[1].ToString();
                            PLCPortsTag tag;
                            if (!PLCPorts.ContainsKey(tagId))
                            {
                                tag = ParseStringAddress(parameters[0].ToString(), (int)parameters[1]);
                                tag.ParentTag = FindParentTag(tag);
                                PLCPorts.Add(tagId, tag);
                            }
                            else
                            {
                                tag = PLCPorts[tagId];
                            }
                            if (!alreadyinitialized)
                            {
                                result.Success = false;
                                result.Result = tag.RisingEdgeDetectedInCurrentCycle;
                            }
                            else
                            {
                                ReadTag(tag);
                                result.Result = tag.RisingEdgeDetectedInCurrentCycle;
                                result.Success = true;
                            }
                            #endregion
                        }
                        break;
                    case "FALLING_EDGE":
                        {
                            #region FALLING_EDGE
                            string tagId = parameters[0].ToString() + "_" + parameters[1].ToString();
                            PLCPortsTag tag;
                            if (!PLCPorts.ContainsKey(tagId))
                            {
                                tag = ParseStringAddress(parameters[0].ToString(), (int)parameters[1]);
                                tag.ParentTag = FindParentTag(tag);
                                PLCPorts.Add(tagId, tag);
                            }
                            else
                            {
                                tag = PLCPorts[tagId];
                            }
                            if (!alreadyinitialized)
                            {
                                result.Success = false;           
                                result.Result = tag.FallingEdgeDetectedInCurrentCycle;
                            }
                            else
                            {
                                ReadTag(tag);
                                result.Result = tag.FallingEdgeDetectedInCurrentCycle;
                                result.Success = true;
                            }
                            #endregion
                        }
                        break;
                    case "EXIT":
                        if (hardwaredriver != null)
                        {
                            hardwaredriver.Close();
                        }
                        RaiseEvent("DISCONNECT", true, null);
                        break;
                }
            }
            catch (Exception ex)
            {
                ExManager.HandleException(ex, "Policy");
                alreadyinitialized = false;
                RaiseEvent("DISCONNECT", true, null);
                if(hardwaredriver!=null)
                    hardwaredriver.Close();    
            }
            return result;
        }

        public void RaiseEvent(string command, bool status, object value)
        {
            StationEventArgs arg = new StationEventArgs(hardwareName, command, status, value);
            if(eventClient!=null)
                eventClient.RaiseEvent(hardwareEvent, arg);
        }

        private Regex AddressRegex = new Regex(
            @"^\s*(?<RegType>(CIO|[ATHCWED]))(?<StartAddress>\d{1,5})(.(?<BitIndex>([0-1][0-9]|1[0-5]|\d{1})))?\s*$");
        private PLCPortsTag ParseStringAddress(string address, int length)
        {
            PLCPortsTag tag =null;
            Match m = AddressRegex.Match(address);
            if (m.Success)
            {
                tag=new PLCPortsTag();
                tag.RegType = m.Groups["RegType"].Value;
                tag.StartAddr = int.Parse(m.Groups["StartAddress"].Value);
                tag.Index = 0;
                if (m.Groups["BitIndex"].Length > 0)
                {
                    tag.Index = int.Parse(m.Groups["BitIndex"].Value);
                    tag.IsBitType = true;
                    tag.ByteData = new byte[2];
                }
                else
                {
                    tag.ByteData = new byte[2 * length];
                }
                tag.Length = length;
            }
            return tag;
        }
        private PLCPortsTag FindParentTag(PLCPortsTag tag)
        {
            int tagEndAddress = tag.StartAddr + tag.Length;
            foreach (PLCPortsTag configTag in ConfigurationPLCPorts.Values)
            {
                int configTagEndAddress = configTag.StartAddr + configTag.Length;
                if ((tag.StartAddr >= configTag.StartAddr) && (tagEndAddress <=configTagEndAddress)&&(configTag.RegType==tag.RegType))
                {
                    return configTag;
                }
            }
            return null;
        }
        private object ReadTag(PLCPortsTag tag)
        {
            object res = null;
            if (tag.AlreadyReadForCycle)
            {
                res=tag.GetData();
            }
            else
            {
                 if (tag.ParentTag != null)
                {
                   ReadTag(tag.ParentTag);
                   tag.ParentTag.GetSubTagValue(tag);    
                }
                else
                {
                    hardwaredriver.Read(tag);           
                }
                res = tag.GetData();
                tag.AlreadyReadForCycle = true;
            }
            return res;
        }
    }
}
